package com.sharp.socket.io.service;

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIONamespace;
import com.corundumstudio.socketio.SocketIOServer;
import com.sharp.authorization.constants.token.TokenItemConst;
import com.sharp.authorization.service.token.TokenService;
import com.sharp.kernel.utils.JsonUtil;
import com.sharp.kernel.utils.StringUtil;
import com.sharp.socket.io.bo.ChatSession;
import com.sharp.socket.io.config.BaseLiveSocketIoConfig;
import com.sharp.socket.io.dao.redis.LiveSessionRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Title: liveSessionService
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2020/9/22 16:19
 * @description Project Name: customer-robot
 * @Package: com.bhfae.customer.robot.service.session
 */
@Slf4j
@Service
public class LiveSessionService {

    private static ConcurrentHashMap<String, ConcurrentHashMap<String, ChatSession>> map = new ConcurrentHashMap<>();
    @Resource
    private TokenService tokenService;
    @Resource
    private SocketIOServer socketIOServer;
    @Resource
    private LiveSessionRepository liveSessionRepository;
    @Autowired
    private BaseLiveSocketIoConfig baseLiveSocketIoConfig;

    public boolean existed(String operator) {
        if (map.get(operator) != null) {
            return MapUtils.isNotEmpty(map.get(operator));
        }
        return false;
    }

    /**
     * @param operator  用户id
     * @param roomId    房间id
     * @param token     token
     * @param sessionId sessionId
     * @return 是否是用户新登录
     */
    public boolean login(String operator, String roomId, String token, UUID sessionId) {
        Double sessionCount = liveSessionRepository.incr(operator, sessionId.toString(), 1);
        log.info("sessionId: {} - 连接个数 {}", sessionId.toString(), StringUtil.parse(sessionCount));
        Long clientNum = liveSessionRepository.size(operator);
        log.info("[用户登录]当前用户[{}]登录数: {}", operator, clientNum);
        String namespace = buildSocketIoNamespace(operator);
        ChatSession liveSession = new ChatSession(namespace, operator, sessionId);
        add(operator, liveSession);
        tokenService.putItem(token, TokenItemConst.SESSION_ID.name(), sessionId.toString());
        return clientNum == 1 && Double.compare(sessionCount, 1) == 0;
    }

    public String buildSocketIoNamespace(String operator) {
        return "/" + operator;
    }

    private synchronized void add(String operator, ChatSession session) {
        ConcurrentHashMap<String, ChatSession> sessionMap = map.getOrDefault(operator, new ConcurrentHashMap<>());
        sessionMap.put(session.getSessionId().toString(), session);
        map.put(operator, sessionMap);
    }

    public boolean hasLogin(String operator) {
        Long clientNum = liveSessionRepository.size(operator);
        log.info("当前用户[{}]登录数: {}", operator, clientNum);
        return new BigDecimal(clientNum).compareTo(BigDecimal.ZERO) > 0;
    }

    public boolean hasLogin(String operator, String sessionId) {
        if (MapUtils.isNotEmpty(map.get(operator))) {
            return map.get(operator).get(sessionId) != null;
        }
        return false;
    }

    /**
     * 登出 多端用户 登出时session个数-1
     *
     * @param operator 用户id
     * @param token    token
     * @return 是否客户彻底退出
     */
    public boolean logoff(String operator, String token, String sessionId) {
        Double sessionCount = liveSessionRepository.incr(operator, sessionId, -1);
        log.info("sessionId: {} - 剩余个数 {}, 是否删除 {}", sessionId, StringUtil.parse(sessionCount), Double.compare(sessionCount, 0) <= 0);
        if (Double.compare(sessionCount, 0) <= 0) {
            liveSessionRepository.remove(operator, sessionId);
            remove(operator, sessionId);
            if (tokenService.isTokenExist(token)) {
                tokenService.removeItem(token, TokenItemConst.SESSION_ID.name());
            }
        }
        Long clientNum = liveSessionRepository.size(operator);
        log.info("[用户登出]当前用户[{}]连接数: {}", operator, clientNum);
        boolean lastDisconnect = clientNum <= 0;
        if (lastDisconnect) {
            liveSessionRepository.delete(operator);
        }
        return lastDisconnect;
    }

    private synchronized void remove(String operator, String sessionId) {
        Map<String, ChatSession> sessionMap = getSession(operator);
        if (sessionMap == null) {
            return;
        }
        sessionMap.remove(sessionId);
    }

    public Map<String, ChatSession> getSession(String operator) {
        return map.get(operator);
    }

    public void kickoff(String operator) {
        Map<String, ChatSession> liveSessionMap = get(operator);
        String namespace = buildSocketIoNamespace(operator);
        if (MapUtils.isEmpty(liveSessionMap)) {
            log.info("用户[{}]已经退出", operator);
        } else {
            for (Map.Entry<String, ChatSession> entry : liveSessionMap.entrySet()) {
                SocketIOClient client = socketIOServer.getNamespace(namespace).getClient(entry.getValue().getSessionId());
                if (client != null) {
                    client.disconnect();
                    liveSessionRepository.remove(operator, client.getSessionId().toString());
                }
            }
        }

    }

    public Map<String, ChatSession> get(String operator) {
        return map.get(operator);
    }

    @PreDestroy
    public void destroy() {
        log.info("关机断开连接{}", JsonUtil.toJson(map));
        if (MapUtils.isNotEmpty(map)) {
            for (Map.Entry<String, ConcurrentHashMap<String, ChatSession>> entry : map.entrySet()) {
                if (MapUtils.isNotEmpty(entry.getValue())) {
                    String operator = entry.getKey();
                    for (Map.Entry<String, ChatSession> sessionEntry : entry.getValue().entrySet()) {
                        log.info("关机断开连接{}-{}", operator, sessionEntry.getKey());
                        liveSessionRepository.remove(operator, sessionEntry.getKey());
                    }
                    Collection<SocketIONamespace> namespaces = socketIOServer.getAllNamespaces();
                    if (CollectionUtils.isNotEmpty(namespaces)) {
                        for (SocketIONamespace namespace : namespaces) {
                            namespace.getRoomOperations(operator).disconnect();
                        }
                    }
                }
            }
        }
    }
}
